package com.changgou.search.service.impl;

import com.alibaba.fastjson.JSON;
import com.changgou.goods.feign.SkuFeign;
import com.changgou.goods.pojo.Sku;
import com.changgou.search.dao.SkuEsMapper;
import com.changgou.search.pojo.SkuInfo;
import com.changgou.search.service.SkuService;
import entity.Result;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.StringTerms;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.SearchResultMapper;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.aggregation.impl.AggregatedPageImpl;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.*;

@Service
public class SkuServiceImpl implements SkuService {
    @Autowired
    private SkuFeign skuFeign;
    @Autowired
    private SkuEsMapper skuEsMapper;
    //工具类
    @Autowired
    private ElasticsearchTemplate elasticsearchTemplate;
    @Override
    public void importData() {
        //1.调用 goods微服务的fegin 查询 符合条件的sku的数据
        Result<List<Sku>> skuResult = skuFeign.findAll();
        //将sku的列表 转换成es中的skuinfo的列表
        List<Sku> data = skuResult.getData();
        //List<Sku> - > Json字符串 [{skuJson}] -> List<SkuInfo>
        List<SkuInfo> skuInfoList = JSON.parseArray(JSON.toJSONString(data), SkuInfo.class);
        for (SkuInfo skuInfo : skuInfoList){
            //获取规格的数据  {"电视音响效果":"立体声","电视屏幕尺寸":"20英寸","尺码":"165"}
            Map<String,Object> map = JSON.parseObject(skuInfo.getSpec(), Map.class);
            //转成MAP  key: 规格的名称  value:规格的选项的值
            skuInfo.setSpecMap(map);
        }

        // 2.调用spring data elasticsearch的API 导入到ES中
        skuEsMapper.saveAll(skuInfoList);
    }

    /**
     * 多条件搜索
     * @param searchMap
     * @return
     */
    @Override
    public Map<String, Object> search(Map<String, String> searchMap) {

        //搜索条件分装
        NativeSearchQueryBuilder builder = buildBasicQuery(searchMap);

        //集合搜索
        HashMap<String, Object> resultMap = searchList(builder);

        //分类分组查询实现,已指定则无需查询
//        if(searchMap == null || StringUtils.isEmpty(searchMap.get("category"))){
//            ArrayList<String> categoryList = searchStringsCategoryList(builder);
//            resultMap.put("categoryList",categoryList);
//        }
//
//        //品牌分组查询,已指定则无需查询
//        if(searchMap == null || StringUtils.isEmpty(searchMap.get("brand"))){
//            ArrayList<String> brandList = searchStringsBrandList(builder);
//            resultMap.put("brandList",brandList);
//        }
//
//        //规格分组查询
//        Map<String, Set<String>> specList = searchStringsSpecList(builder);
//        resultMap.put("specList",specList);

        //分组查询汇总
        Map<String, Object> groupMap = searchGroupList(builder, searchMap);
        resultMap.putAll(groupMap);
        //返回分页初始信息
        //分页
        //开启分页查询
        String pageSize = searchMap.get("pageSize"); //每页显示多少条
        if (StringUtils.isEmpty(pageSize)){
            pageSize="30";
        }
        Integer pageNum=converterPage(searchMap);//默认第一页
        //当前页
        resultMap.put("pageNum",pageNum);
        resultMap.put("pageSize",pageSize);
        return resultMap;
    }

    /**
     * 根据名字搜索
     * @param searchMap
     * @return
     */
    private NativeSearchQueryBuilder buildBasicQuery(Map<String, String> searchMap) {
        //高级搜索条件,为必需项
        NativeSearchQueryBuilder builder=new NativeSearchQueryBuilder();
        //组合条件搜索
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
        if(searchMap!=null && searchMap.size()>0){
            //根据关键词搜索
            //1.获取关键字的值
            String keywords = searchMap.get("keywords");
            //根据关键字查询
            if (!StringUtils.isEmpty(keywords)) {
    //            keywords = "华为";//赋值给一个默认的值
                //3.设置查询的条件
//                builder.withQuery(QueryBuilders.matchQuery("name", keywords));
                boolQueryBuilder .must(QueryBuilders.queryStringQuery(keywords).field("name"));
            }
            //根据分类查询
            if (!StringUtils.isEmpty(searchMap.get("category"))) {
                // termQuery 不分词
                boolQueryBuilder .must(QueryBuilders.termQuery("categoryName",searchMap.get("category")));
            }
            //根据品牌查询
            if (!StringUtils.isEmpty(searchMap.get("brand"))) {
                // termQuery 不分词
                boolQueryBuilder .must(QueryBuilders.termQuery("brandName",searchMap.get("brand")));
            }
            //规格spec筛选,spec_网络=联通
            for (Map.Entry<String, String> entry : searchMap.entrySet()) {
                String key=entry.getKey();
                if(key.startsWith("spec_")){
                    //规格条件的值
                    String value=entry.getValue();
                        // termQuery 不分词
                        boolQueryBuilder .must(QueryBuilders.termQuery("specMap."+key.substring(5)+".keyword",value));
                }
            }
            //根据价格筛序
            //前端参数  price 0-500元 500-1000元 ... 3000元以上
            //按照-分割 [0,500] [500,1000]
            String price=searchMap.get("price");
            if(!StringUtils.isEmpty(price)){
                price=price.replace("元","").replace("以上","");
                String[] prices=price.split("-");
                if(prices != null && prices.length>0){
                    //price > price[0]
                    boolQueryBuilder .must(QueryBuilders.rangeQuery("price").gt(Integer.parseInt(prices[0])));
                    //price <= price[1]
                    if(prices.length==2){
                        boolQueryBuilder .must(QueryBuilders.rangeQuery("price").lte(Integer.parseInt(prices[1])));
                    }
                }
            }
            //排序
            String sortField=searchMap.get("sortField");
            String sortRule=searchMap.get("sortRule");
            if (!StringUtils.isEmpty(sortField) && !StringUtils.isEmpty(sortRule)){
                builder.withSort(new FieldSortBuilder(sortField).order(SortOrder.valueOf(sortRule)));
            }

            //分页
            //开启分页查询
            String pageSize = searchMap.get("pageSize"); //每页显示多少条
            if (StringUtils.isEmpty(pageSize)){
                pageSize="30";
            }
            Integer pageNum=converterPage(searchMap);//默认第一页
            //分页
            builder.withPageable(PageRequest.of(pageNum-1,Integer.parseInt(pageSize)));
        }


        //合并条件查询
        builder.withQuery(boolQueryBuilder);

        return builder;
    }

    /**
     * 接受前端传入的分页参数
     * @param searchMap
     * @return
     */
    private Integer converterPage(Map<String,String> searchMap){
        if(searchMap != null){
            String pageNum=searchMap.get("pageNum");
            try {
                return Integer.parseInt(pageNum);
            } catch (NumberFormatException e) {
            }
        }
        return 1;
    }


    private HashMap<String, Object> searchList(NativeSearchQueryBuilder builder) {
        //高亮配置
        HighlightBuilder.Field field = new HighlightBuilder.Field("name");//指定高亮域
        //<em style="color:red;">
        field.preTags("<em style=\"color:red;\">");
        field.postTags("</em>");
        //碎片长度
        field.fragmentSize(100);
        //添加高亮
        builder.withHighlightFields(field);

        //搜索
        /**
         *  builder.build() 搜索条件封装
         *  SkuInfo.class   结果集需要转换的数据类型
         *  AggregatedPage<SkuInfo> 结果集的封装
         *  new SearchResultMapper  搜索后的封装对象
         */
//        AggregatedPage<SkuInfo> page = elasticsearchTemplate.queryForPage(builder.build(), SkuInfo.class);
        AggregatedPage<SkuInfo> page = elasticsearchTemplate.queryForPage(
                builder.build(),
                SkuInfo.class,
                new SearchResultMapper() {
                    @Override
                    public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) {
                        //存所有转换后的高亮数据
                        ArrayList<T> list = new ArrayList<>();

                        //搜索结果中含有高亮数据和非高亮数据,需要分析结果集
                        //获取所有数据,含有高亮数据和非高亮数据
                        SearchHits hits = response.getHits();
                        for (SearchHit hit : hits) {
                            //获取非高亮数据
                            SkuInfo skuInfo=JSON.parseObject(hit.getSourceAsString(),SkuInfo.class);
                            //获取高亮数据
                            HighlightField highlightField = hit.getHighlightFields().get("name");
                            if(highlightField != null && highlightField.getFragments() !=null){
                                Text[] fragments = highlightField.getFragments();
                                StringBuffer buffer=new StringBuffer();
                                for (Text fragment : fragments) {
                                    buffer.append(fragment.toString());
                                }
                                //非高亮数据替换成高亮数据
                                skuInfo.setName(buffer.toString());
                            }
                            //数据返回
                            list.add((T) skuInfo);
                        }
                        return new AggregatedPageImpl<T>(list,pageable,response.getHits().getTotalHits());
                    }
                });

        //总记录数
        long totalElements = page.getTotalElements();
        //总页数
        int totalPages = page.getTotalPages();
        //获取结果集
        List<SkuInfo> content = page.getContent();
        //封装map存数据
        HashMap<String, Object> resultMap = new HashMap<>();
        resultMap.put("rows",content);
        resultMap.put("total",totalElements);//总记录数
        resultMap.put("totalPages",totalPages);
        //分页
//        NativeSearchQuery query = builder.build();
//        Pageable pageable = query.getPageable();
//        Pageable pageable = page.getPageable();
//        Integer pagesize=pageable.getPageSize();//总页数
//        Integer pageNumber=pageable.getPageNumber();//当前页
//        resultMap.put("pagesize",pagesize);
//        resultMap.put("pageNumber",pageNumber);
//        resultMap.put("pageNum",builder.build().getPageable().getPageNumber()+1);
//        resultMap.put("pageNum",builder.build().getPageable().getPageNumber()+1);
//        resultMap.put("pageSize",builder.build().getPageable().getPageSize());

        return resultMap;
    }
    /***
     * 分组查询汇总
     * @param builder
     * @return
     */
    private Map<String,Object> searchGroupList(NativeSearchQueryBuilder builder,Map<String, String> searchMap) {
        //存数据
        Map<String,Object> groupMapResult = new HashMap<>();

        //添加聚合操作,用于输出字段
        //terms  指定分组的一个别名
        //field 指定要分组的字段名
        // size 指定查询结果的数量 默认是10个
        if(searchMap == null || StringUtils.isEmpty(searchMap.get("category"))) {
            builder.addAggregation(AggregationBuilders.terms("skuCategory").field("categoryName"));
        }
        if(searchMap == null || StringUtils.isEmpty(searchMap.get("brand"))) {
            builder.addAggregation(AggregationBuilders.terms("skuBrand").field("brandName"));
        }
        builder.addAggregation(AggregationBuilders.terms("skuSpec").field("spec.keyword"));
        AggregatedPage<SkuInfo> aggregatedPage= elasticsearchTemplate.queryForPage(builder.build(), SkuInfo.class);

        //获取聚合聚合数据集合
        if(searchMap == null || StringUtils.isEmpty(searchMap.get("category"))) {
            StringTerms categoryTerms = aggregatedPage.getAggregations().get("skuCategory");
            List<String> categoryList = getGroupList(categoryTerms);
            groupMapResult.put("categoryList",categoryList);
        }
        if(searchMap == null || StringUtils.isEmpty(searchMap.get("brand"))) {
            StringTerms brandTerms = aggregatedPage.getAggregations().get("skuBrand");
            List<String> brandList = getGroupList(brandTerms);
            groupMapResult.put("brandList",brandList);
        }
        StringTerms specTerms = aggregatedPage.getAggregations().get("skuSpec");
        //分别获取对应List数据
        List<String> specList = getGroupList(specTerms);
        Map<String, Set<String>> specMap = putAllSpec(specList);
        groupMapResult.put("specList",specMap);
        return groupMapResult;
    }

    /**
     * 组合查询抽取
     * @param stringTerms
     * @return
     */
    public List<String> getGroupList(StringTerms stringTerms){
        ArrayList<String> groupList = new ArrayList<>();
        for (StringTerms.Bucket bucket : stringTerms.getBuckets()){
            groupList.add(bucket.getKeyAsString());
        }
        return groupList;
    }

    /***
     * 根据分类搜索
     * @param builder
     * @return
     */
    private ArrayList<String> searchStringsCategoryList(NativeSearchQueryBuilder builder) {
        //添加聚合操作,用于输出字段
        // 4.1 商品分类的列表展示: 按照商品分类的名称来分组
        //terms  指定分组的一个别名
        //field 指定要分组的字段名
        // size 指定查询结果的数量 默认是10个
        builder.addAggregation(AggregationBuilders.terms("skuCategory").field("categoryName"));
        AggregatedPage<SkuInfo> aggregatedPage= elasticsearchTemplate.queryForPage(builder.build(), SkuInfo.class);

        //获取聚合聚合数据集合
        StringTerms stringTerms = aggregatedPage.getAggregations().get("skuCategory");
        ArrayList<String> categoryList = new ArrayList<>();
        for (StringTerms.Bucket bucket : stringTerms.getBuckets()){
            //获取其中一个分类名字
            categoryList.add(bucket.getKeyAsString());
        }
        return categoryList;
    }
    /***
     * 根据品牌搜索
     * @param builder
     * @return
     */
    private ArrayList<String> searchStringsBrandList(NativeSearchQueryBuilder builder) {
        //添加聚合操作,用于输出字段
        // 4.1 商品分类的列表展示: 按照商品分类的名称来分组
        //terms  指定分组的一个别名
        //field 指定要分组的字段名
        // size 指定查询结果的数量 默认是10个
        builder.addAggregation(AggregationBuilders.terms("skuBrand").field("brandName"));
        AggregatedPage<SkuInfo> aggregatedPage= elasticsearchTemplate.queryForPage(builder.build(), SkuInfo.class);

        //获取聚合聚合数据集合
        StringTerms stringTerms = aggregatedPage.getAggregations().get("skuBrand");
        ArrayList<String> brandList = new ArrayList<>();
        for (StringTerms.Bucket bucket : stringTerms.getBuckets()){
            //获取其中一个品牌名字
            brandList.add(bucket.getKeyAsString());
        }
        return brandList;
    }
    /***
     * 根据规格搜索
     * @param builder
     * @return
     */
    private Map<String, Set<String>> searchStringsSpecList(NativeSearchQueryBuilder builder) {
        //添加聚合操作,用于输出字段
        // 4.1 商品分类的列表展示: 按照商品分类的名称来分组
        //terms  指定分组的一个别名
        //field 指定要分组的字段名
        // size 指定查询结果的数量 默认是10个
        // spec.keyword 拒绝自动分词

        //重要::::::Bug默认显示10条,将规格导致聚合查询有疏漏,加size()

        builder.addAggregation(AggregationBuilders.terms("skuSpec").field("spec.keyword").size(10000));
        AggregatedPage<SkuInfo> aggregatedPage= elasticsearchTemplate.queryForPage(builder.build(), SkuInfo.class);

        //获取聚合聚合数据集合
        StringTerms stringTerms = aggregatedPage.getAggregations().get("skuSpec");
        ArrayList<String> specList = new ArrayList<>();
        for (StringTerms.Bucket bucket : stringTerms.getBuckets()){
            //获取其中一个规格名字
            specList.add(bucket.getKeyAsString());
        }
        Map<String, Set<String>> allSpec = putAllSpec(specList);

        return allSpec;
    }

    /**
     * 规格数据转换
     * @param specList
     * @return
     */
    private Map<String, Set<String>> putAllSpec(List<String> specList) {
        //合并后的map对象
        HashMap<String, Set<String>> allSpec = new HashMap<>();
        //循环specList
        for(String spec : specList){
            //将每个json字符串转成map
            Map<String,String> specMap = JSON.parseObject(spec, Map.class);
            //合并
            for (Map.Entry<String, String> entry : specMap.entrySet()) {
                String key=entry.getKey();//规格名
                String value=entry.getValue();//规格值
                //合并
                Set<String> specSet = allSpec.get(key);
                if(specSet == null){
                    specSet=new HashSet<String>();
                }
                specSet.add(value);
                allSpec.put(key,specSet);
            }
        }
        return allSpec;
    }
}
